package Olivia.olivia3d.camera
{	
	import Olivia.olivia3d.Olivia3D;
	
	import com.adobe.utils.PerspectiveMatrix3D;
	
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.EventDispatcher;
	import flash.events.MouseEvent;
	import flash.geom.Matrix3D;
	import flash.geom.Point;
	import flash.geom.Vector3D;
	
	public class OCamera extends EventDispatcher
	{
		public var viewWidth:Number = 800;
		public var viewHeight:Number = 600;
		public var zNear:Number = 1;
		public var zFar:Number = 500;		
		public var fov:Number = 90*Math.PI/180;
		
		//world mat
		public var world:Matrix3D;
		//view mat
		public var LR:Boolean;			//t==lh,f==rh;
		public var viewEye:Vector3D;
		public var viewAt:Vector3D;
		public var viewUp:Vector3D;
		//projection mat
		public var projection:PerspectiveMatrix3D; 
		
		private var _stage:Stage;
		
		public function OCamera(s:Stage=null)
		{
			init(s);
		}
		
		protected function init(s:Stage):void 
		{			
			world = new Matrix3D();
			LR = false;
			viewEye = new Vector3D();
			viewAt = new Vector3D();
			viewUp = new Vector3D(0,1,0);
			projection = new PerspectiveMatrix3D();
			projection.perspectiveFieldOfViewRH(fov,viewWidth/viewHeight,zNear,zFar);
			
			if(s)
				addControler(s);
		}
		
		public function getWorld(pos:Vector3D):Matrix3D
		{
			var reMat:Matrix3D = new Matrix3D();
			reMat.appendRotation(pos.w,Vector3D.Y_AXIS);
			reMat.appendTranslation(pos.x,pos.y,pos.z);
			reMat.append(world);
			return reMat;
		}
		
		public function getWorldViewMat(pos:Vector3D):Matrix3D
		{
			var reMat:Matrix3D = new Matrix3D();
			reMat.appendRotation(pos.w,Vector3D.Y_AXIS);
			reMat.appendTranslation(pos.x,pos.y,pos.z);
			reMat.append(world);
			reMat.append(view);		
			return reMat;
		}
		
		public function getWorldViewProjMat(pos:Vector3D):Matrix3D
		{
			var reMat:Matrix3D = new Matrix3D();
			reMat.appendRotation(pos.w,Vector3D.Y_AXIS);
			reMat.appendTranslation(pos.x,pos.y,pos.z);
			reMat.append(world);
			reMat.append(view);
			reMat.append(projection);
			return reMat;
		}		
		
		public function get view():Matrix3D
		{
			return LR?lookAtLH(viewEye, viewAt, viewUp):lookAtRH(viewEye, viewAt, viewUp);
		}
		
		public function lookAtLH(eye:Vector3D, at:Vector3D, up:Vector3D):Matrix3D
		{
			//			zaxis = normal(cameraPosition - cameraTarget)
			var vz:Vector3D = at.subtract(eye);
			vz.normalize();
			//			xaxis = normal(cross(cameraUpVector, zaxis))
			var vx:Vector3D = up.crossProduct(vz);
			vx.normalize();
			//			yaxis = cross(zaxis, xaxis)
			var vy:Vector3D = vz.crossProduct(vx);
			
			var vw:Vector3D = new Vector3D(
				-vx.dotProduct(eye),
				-vy.dotProduct(eye),
				-vz.dotProduct(eye),
				1);
			
			var rema:Matrix3D = new Matrix3D();
			rema.copyRowFrom(0,vx);
			rema.copyRowFrom(1,vy);
			rema.copyRowFrom(2,vz);
			rema.copyColumnFrom(3,vw);
			//			xaxis.x           yaxis.x           zaxis.x          0
			//			xaxis.y           yaxis.y           zaxis.y          0
			//			xaxis.z           yaxis.z           zaxis.z          0
			//			-dot(xaxis, cameraPosition)  -dot(yaxis, cameraPosition)  -dot(zaxis, cameraPosition)  1
			return rema;
		}
		
		public function lookAtRH(eye:Vector3D, at:Vector3D, up:Vector3D):Matrix3D
		{
			//			zaxis = normal(cameraPosition - cameraTarget)
			var vz:Vector3D = eye.subtract(at);
			vz.normalize();
			//			xaxis = normal(cross(cameraUpVector, zaxis))
			var vx:Vector3D = up.crossProduct(vz);
			vx.normalize();
			//			yaxis = cross(zaxis, xaxis)
			var vy:Vector3D = vz.crossProduct(vx);
			
			var vw:Vector3D = new Vector3D(
				-vx.dotProduct(eye),
				-vy.dotProduct(eye),
				-vz.dotProduct(eye),
				1);
			
			var rema:Matrix3D = new Matrix3D();
			rema.copyRowFrom(0,vx);
			rema.copyRowFrom(1,vy);
			rema.copyRowFrom(2,vz);
			rema.copyColumnFrom(3,vw);
			//			xaxis.x           yaxis.x           zaxis.x          0
			//			xaxis.y           yaxis.y           zaxis.y          0
			//			xaxis.z           yaxis.z           zaxis.z          0
			//			-dot(xaxis, cameraPosition)  -dot(yaxis, cameraPosition)  -dot(zaxis, cameraPosition)  1
			return rema;
		}
				
		private var pos:Point;
		private var r:Number;
		private function addControler(s:Stage):void
		{
			// TODO Auto Generated method stub
			_stage = s;
			_stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
			_stage.addEventListener(MouseEvent.MOUSE_WHEEL, onWheel);
		}
		
		protected function onDown(event:MouseEvent):void
		{
			// TODO Auto-generated method stub			
			pos = new Point(_stage.mouseX,_stage.mouseY);
			_stage.removeEventListener(MouseEvent.MOUSE_DOWN, onDown);
			_stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);
			_stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
		}
		
		protected function onUp(event:MouseEvent):void
		{
			// TODO Auto-generated method stub		
			_stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMove);
			_stage.removeEventListener(MouseEvent.MOUSE_UP,onUp);
			_stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
		}
		
		protected function onMove(event:MouseEvent):void
		{
			// TODO Auto-generated method stub
			if(event.buttonDown){
				var angel:Number = Math.PI*(event.localX-pos.x)/360;
				//r = math.sqt((ex - ax)^2 + (ez - az)^2); 
				r = Math.sqrt(Math.pow(viewEye.x-viewAt.x,2)+Math.pow(viewEye.z-viewAt.z,2));
				viewEye.x = viewAt.x + r*Math.cos(Math.atan2(viewEye.z,viewEye.x)+angel);
				viewEye.z = viewAt.z + r*Math.sin(Math.atan2(viewEye.z,viewEye.x)+angel);				
				pos.x = event.localX;
			}
		}
		
		protected function onWheel(event:MouseEvent):void
		{
			// TODO Auto-generated method stub
			var d:int = event.delta;
			viewEye.y -= d;
			if(viewEye.y < 20)
				viewEye.y = 20;
		}
	}
}